/*-------------------------------------------------------------------------
 *
 * inherit.c
 *	  处理继承树中的子关系的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/optimizer/util/inherit.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/partition.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/appendinfo.h"
#include "optimizer/inherit.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/plancat.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "parser/parsetree.h"
#include "partitioning/partdesc.h"
#include "partitioning/partprune.h"
#include "utils/rel.h"


static void fc_expand_partitioned_rtentry(PlannerInfo *fc_root, RelOptInfo *fc_relinfo,
									   RangeTblEntry *fc_parentrte,
									   Index fc_parentRTindex, Relation fc_parentrel,
									   PlanRowMark *fc_top_parentrc, LOCKMODE fc_lockmode);
static void fc_expand_single_inheritance_child(PlannerInfo *fc_root,
											RangeTblEntry *fc_parentrte,
											Index fc_parentRTindex, Relation fc_parentrel,
											PlanRowMark *fc_top_parentrc, Relation fc_childrel,
											RangeTblEntry **fc_childrte_p,
											Index *fc_childRTindex_p);
static Bitmapset *fc_translate_col_privs(const Bitmapset *fc_parent_privs,
									  List *fc_translated_vars);
static Bitmapset *fc_translate_col_privs_multilevel(PlannerInfo *fc_root,
												 RelOptInfo *fc_rel,
												 RelOptInfo *fc_parent_rel,
												 Bitmapset *fc_parent_cols);
static void fc_expand_appendrel_subquery(PlannerInfo *fc_root, RelOptInfo *fc_rel,
									  RangeTblEntry *fc_rte, Index fc_rti);


/*
 * expand_inherited_rtentry
 *		扩展具有“inh”位设置的rangetable条目。
 *
 * “inh”仅在两种情况下被允许：RELATION和SUBQUERY RTE。
 *
 * 在普通RELATION RTE上“inh”意味着它是一个分区表或传统继承集合的
 * 父表。在这种情况下，我们必须为所有有趣的子表添加条目
 * 到查询的rangetable中，并为它们构建
 * 额外的规划器数据结构，包括RelOptInfos，
 * AppendRelInfos，以及可能的PlanRowMarks。
 *
 * 请注意，原始RTE被视为代表整个继承
 * 集合。在传统继承的情况下，生成的第一个
 * RTE是针对同一表的RTE，但inh = false，以表示
 * 在继承集中的简单成员角色的父表。对于
 * 分区来说，我们不需要第二个RTE，因为分区表
 * 本身没有数据，且不需要被扫描。
 *
 * SUBQUERY RTE上的“inh”意味着它是UNION ALL组的父项，
 * 这被视为类似于继承案例的appendrel；然而，
 * 我们已经为子查询创建了RTE和AppendRelInfos。我们只需
 * 为它们构建RelOptInfos，这通过expand_appendrel_subquery完成。
 */
void expand_inherited_rtentry(PlannerInfo *fc_root, RelOptInfo *fc_rel,
						 RangeTblEntry *fc_rte, Index fc_rti)
{
	Oid			fc_parentOID;
	Relation	fc_oldrelation;
	LOCKMODE	fc_lockmode;
	PlanRowMark *fc_oldrc;
	bool		fc_old_isParent = false;
	int			fc_old_allMarkTypes = 0;

	Assert(fc_rte->inh);			/*
		 * 我们使用 CaseTestExpr 作为强制转换树的基础，
		 * 因为插入源值是非常便宜的。
		 */

	if (fc_rte->rtekind == RTE_SUBQUERY)
	{
		fc_expand_appendrel_subquery(fc_root, fc_rel, fc_rte, fc_rti);
		return;
	}

	Assert(fc_rte->rtekind == RTE_RELATION);

	fc_parentOID = fc_rte->relid;

	/*
	 * 我们过去在这里检查has_subclass()，但现在不再需要
	 * 这样做，因为subquery_planner已经完成了。
	 */

	/*
	 * 重写器应该已经对查询中命名的每个关系获得了适当的锁，
	 * 因此我们可以在不锁定的情况下打开父关系。然而，对于
	 * 我们添加到查询中的每个子关系，我们必须获得适当的锁，
	 * 因为这将是这些关系在解析/重写/计划管道中的首次使用。
	 * 子关系应该使用与其父关系相同的锁模式。
	 */
	fc_oldrelation = table_open(fc_parentOID, NoLock);
	fc_lockmode = fc_rte->rellockmode;

	/*
	 * 如果父关系被选择为FOR UPDATE/SHARE，我们需要将其
	 * PlanRowMark标记为isParent = true，并为每个子关系生成一个新的
	 * PlanRowMark。
	 */
	fc_oldrc = get_plan_rowmark(fc_root->rowMarks, fc_rti);
	if (fc_oldrc)
	{
		fc_old_isParent = fc_oldrc->isParent;
		fc_oldrc->isParent = true;
		/* 在子项添加到allMarkTypes之前保存其初始值 */
		fc_old_allMarkTypes = fc_oldrc->allMarkTypes;
	}

	/* 扫描继承集并扩展它 */
	if (fc_oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
	{
		/*
		 * 分区表，因此要进行分区设置。
		 */
		Assert(fc_rte->relkind == RELKIND_PARTITIONED_TABLE);

		/*
		 * 递归扩展并锁定分区。同时提取所有分区表的分区键列。
		 */
		fc_expand_partitioned_rtentry(fc_root, fc_rel, fc_rte, fc_rti,
								   fc_oldrelation, fc_oldrc, fc_lockmode);
	}
	else
	{
		/*
		 * 普通表，因此处理传统继承子表。（请注意，分区表不允许有继承子表，因此两种情况不可能同时适用。）
		 */
		List	   *fc_inhOIDs;
		ListCell   *fc_l;

		/* 扫描所有继承集的成员，获取所需的锁 */
		fc_inhOIDs = find_all_inheritors(fc_parentOID, fc_lockmode, NULL);

		/*
		 * 我们曾经特殊处理表没有任何子表的情况，通过清空 rte->inh 并退出。但是这已经不再可行，因为这个函数不在根据 rte->inh 做出决策之后运行。我们必须将这种情况视为正常继承。表本身应该始终可以找到。
		 */
		Assert(fc_inhOIDs != NIL);
		Assert(linitial_oid(fc_inhOIDs) == fc_parentOID);

		/* 扩展 simple_rel_array 和相关对象以容纳子对象。 */
		expand_planner_arrays(fc_root, list_length(fc_inhOIDs));

		/*
		 * 按照 find_all_inheritors 返回的 OID 顺序扩展继承子项。
		 */
		foreach(fc_l, fc_inhOIDs)
		{
			Oid			fc_childOID = lfirst_oid(fc_l);
			Relation	fc_newrelation;
			RangeTblEntry *fc_childrte;
			Index		fc_childRTindex;

			/* 如果需要则打开 rel；我们已经有所需的锁定 */
			if (fc_childOID != fc_parentOID)
				fc_newrelation = table_open(fc_childOID, NoLock);
			else
				fc_newrelation = fc_oldrelation;

			/*
			 * 父表可能有属于其他后端的临时表。我们不能安全地访问这样的表（因为缓冲问题），最好的做法似乎是默默忽略它们。
			 */
			if (fc_childOID != fc_parentOID && RELATION_IS_OTHER_TEMP(fc_newrelation))
			{
				table_close(fc_newrelation, fc_lockmode);
				continue;
			}

			/* 创建 RTE 和 AppendRelInfo，如有需要还要创建 PlanRowMark。 */
			fc_expand_single_inheritance_child(fc_root, fc_rte, fc_rti, fc_oldrelation,
											fc_oldrc, fc_newrelation,
											&fc_childrte, &fc_childRTindex);

			/* 创建其他关系 RelOptInfo。 */
			(void) build_simple_rel(fc_root, fc_childRTindex, fc_rel);

			/* 关闭子关系，但保持锁定 */
			if (fc_childOID != fc_parentOID)
				table_close(fc_newrelation, NoLock);
		}
	}

	/*
	 * 一些子项可能需要不同的标记类型，这将在 oldrc 中报告。如果是这样，将相关条目添加到顶级目标列表并更新父关系的 reltarget。这应该与 preprocess_targetlist() 在请求标记类型时所添加的内容匹配。
	 *
	 * （有一天将这些 resjunk 列合并到用于 UPDATE/DELETE 的行标识列管理中可能会很有用。但是今天不是那一天。）
	 */
	if (fc_oldrc)
	{
		int			fc_new_allMarkTypes = fc_oldrc->allMarkTypes;
		Var		   *fc_var;
		TargetEntry *fc_tle;
		char		fc_resname[32];
		List	   *fc_newvars = NIL;

		/* 如果需要则添加 TID 垃圾 Var，除非我们已经有了它 */
		if (fc_new_allMarkTypes & ~(1 << ROW_MARK_COPY) &&
			!(fc_old_allMarkTypes & ~(1 << ROW_MARK_COPY)))
		{
			/* 需要获取 TID */
			fc_var = makeVar(fc_oldrc->rti,
						  SelfItemPointerAttributeNumber,
						  TIDOID,
						  -1,
						  InvalidOid,
						  0);
			snprintf(fc_resname, sizeof(fc_resname), "ctid%u", fc_oldrc->rowmarkId);
			fc_tle = makeTargetEntry((Expr *) fc_var,
								  list_length(fc_root->processed_tlist) + 1,
								  pstrdup(fc_resname),
								  true);
			fc_root->processed_tlist = lappend(fc_root->processed_tlist, fc_tle);
			fc_newvars = lappend(fc_newvars, fc_var);
		}

		/* 如果需要则添加整行垃圾 Var，除非我们已经有了它 */
		if ((fc_new_allMarkTypes & (1 << ROW_MARK_COPY)) &&
			!(fc_old_allMarkTypes & (1 << ROW_MARK_COPY)))
		{
			fc_var = makeWholeRowVar(planner_rt_fetch(fc_oldrc->rti, fc_root),
								  fc_oldrc->rti,
								  0,
								  false);
			snprintf(fc_resname, sizeof(fc_resname), "wholerow%u", fc_oldrc->rowmarkId);
			fc_tle = makeTargetEntry((Expr *) fc_var,
								  list_length(fc_root->processed_tlist) + 1,
								  pstrdup(fc_resname),
								  true);
			fc_root->processed_tlist = lappend(fc_root->processed_tlist, fc_tle);
			fc_newvars = lappend(fc_newvars, fc_var);
		}

		/* 添加 tableoid 垃圾 Var，除非我们已经有了它 */
		if (!fc_old_isParent)
		{
			fc_var = makeVar(fc_oldrc->rti,
						  TableOidAttributeNumber,
						  OIDOID,
						  -1,
						  InvalidOid,
						  0);
			snprintf(fc_resname, sizeof(fc_resname), "tableoid%u", fc_oldrc->rowmarkId);
			fc_tle = makeTargetEntry((Expr *) fc_var,
								  list_length(fc_root->processed_tlist) + 1,
								  pstrdup(fc_resname),
								  true);
			fc_root->processed_tlist = lappend(fc_root->processed_tlist, fc_tle);
			fc_newvars = lappend(fc_newvars, fc_var);
		}

		/*
		 * 将新添加的 Vars 添加到父的 reltarget 中。我们不必担心子表的 reltargets，它们将稍后生成。
		 */
		add_vars_to_targetlist(fc_root, fc_newvars, bms_make_singleton(0), false);
	}

	table_close(fc_oldrelation, NoLock);
}

/*
 * expand_partitioned_rtentry
 *		递归扩展分区表的 RTE。
 */
static void fc_expand_partitioned_rtentry(PlannerInfo *fc_root, RelOptInfo *fc_relinfo,
						   RangeTblEntry *fc_parentrte,
						   Index fc_parentRTindex, Relation fc_parentrel,
						   PlanRowMark *fc_top_parentrc, LOCKMODE fc_lockmode)
{
	PartitionDesc fc_partdesc;
	Bitmapset  *fc_live_parts;
	int			fc_num_live_parts;
	int			fc_i;

	check_stack_depth();

	Assert(fc_parentrte->inh);

	fc_partdesc = PartitionDirectoryLookup(fc_root->glob->partition_directory,
										fc_parentrel);

	/* 分区表应该始终有一个分区描述符。 */
	Assert(fc_partdesc);

	/*
	 * 注意是否有任何分区键列正在被更新。尽管我们关心的是根分区表的 updatedCols，但我们使用 parentrte 来获取 updatedCols。这是方便的，因为 parentrte 已经将根 partrel 的 updatedCols 转换为匹配 parentrel 的属性顺序。
	 */
	if (!fc_root->partColsUpdated)
		fc_root->partColsUpdated =
			has_partition_attrs(fc_parentrel, fc_parentrte->updatedCols, NULL);

	/* 如果没有分区，这里没有进一步的工作要做。 */
	if (fc_partdesc->nparts == 0)
		return;

	/*
	 * 使用分配给父关系的限制条件执行分区修剪。live_parts 将包含存活分区的 PartitionDesc 索引。下面，我们将为存活的分区初始化子对象。
	 */
	fc_relinfo->live_parts = fc_live_parts = prune_append_rel_partitions(fc_relinfo);

	/* 扩展 simple_rel_array 和相关对象以容纳子对象。 */
	fc_num_live_parts = bms_num_members(fc_live_parts);
	if (fc_num_live_parts > 0)
		expand_planner_arrays(fc_root, fc_num_live_parts);

	/*
	 * 我们还在父关系中存储分区 RelOptInfo 指针。由于我们正在进行 palloc0，因此对应于修剪分区的插槽将包含 NULL。
	 */
	Assert(fc_relinfo->part_rels == NULL);
	fc_relinfo->part_rels = (RelOptInfo **)
		palloc0(fc_relinfo->nparts * sizeof(RelOptInfo *));

	/*
	 * 为每个存活的分区创建一个子 RTE。请注意，与传统继承不同，我们不需要为分区表本身创建子 RTE，因为它不会被扫描。
	 */
	fc_i = -1;
	while ((fc_i = bms_next_member(fc_live_parts, fc_i)) >= 0)
	{
		Oid			fc_childOID = fc_partdesc->oids[fc_i];
		Relation	fc_childrel;
		RangeTblEntry *fc_childrte;
		Index		fc_childRTindex;
		RelOptInfo *fc_childrelinfo;

		/*
		 * 打开关系，获取所需的锁定。如果一个分区最近被分离并随后被删除，那么打开它将失败。在这种情况下，表现得像是该分区已被修剪。
		 */
		fc_childrel = try_table_open(fc_childOID, fc_lockmode);
		if (fc_childrel == NULL)
		{
			fc_relinfo->live_parts = bms_del_member(fc_relinfo->live_parts, fc_i);
			continue;
		}

		/*
		 * 属于其他会话的临时分区在定义上应该被禁止，但出于小心起见，让我们再检查一遍。
		 */
		if (RELATION_IS_OTHER_TEMP(fc_childrel))
			elog(ERROR, "temporary relation from another session found as partition");

		/* 创建 RTE 和 AppendRelInfo，如有需要还要创建 PlanRowMark。 */
		fc_expand_single_inheritance_child(fc_root, fc_parentrte, fc_parentRTindex,
										fc_parentrel, fc_top_parentrc, fc_childrel,
										&fc_childrte, &fc_childRTindex);

		/* 创建其他关系 RelOptInfo。 */
		fc_childrelinfo = build_simple_rel(fc_root, fc_childRTindex, fc_relinfo);
		fc_relinfo->part_rels[fc_i] = fc_childrelinfo;
		fc_relinfo->all_partrels = bms_add_members(fc_relinfo->all_partrels,
												fc_childrelinfo->relids);

		/* 如果这个子项本身是分区的，则递归 */
		if (fc_childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
			fc_expand_partitioned_rtentry(fc_root, fc_childrelinfo,
									   fc_childrte, fc_childRTindex,
									   fc_childrel, fc_top_parentrc, fc_lockmode);

		/* 关闭子关系，但保持锁定 */
		table_close(fc_childrel, NoLock);
	}
}


static void fc_expand_single_inheritance_child(PlannerInfo *fc_root, RangeTblEntry *fc_parentrte,
								Index fc_parentRTindex, Relation fc_parentrel,
								PlanRowMark *fc_top_parentrc, Relation fc_childrel,
								RangeTblEntry **fc_childrte_p,
								Index *fc_childRTindex_p)
{
	Query	   *fc_parse = fc_root->parse;
	Oid			fc_parentOID = RelationGetRelid(fc_parentrel);
	Oid			fc_childOID = RelationGetRelid(fc_childrel);
	RangeTblEntry *fc_childrte;
	Index		fc_childRTindex;
	AppendRelInfo *fc_appinfo;
	TupleDesc	fc_child_tupdesc;
	List	   *fc_parent_colnames;
	List	   *fc_child_colnames;

	/*
	 * 为子项构建一个 RTE，并附加到查询的 rangetable 列表中。我们
	 * 复制父项 RTE 大部分标量字段，但替换子项的关系 OID、
	 * relkind 和 inh。同时，将 requiredPerms 设置为零，因为
	 * 所有的必需权限检查都是在原始 RTE 上完成的。同样，
	 * 将子项的 securityQuals 设置为空，因为我们只希望应用
	 * 父项的 RLS 条件，而不管各个子项可能具有的 RLS 属性。
	 * 这是一个有意的选择，使得
	 * 继承的 RLS 像常规权限检查一样工作。父项的
	 * securityQuals 将与其他基本限制子句一起传播给子项，
	 * 所以我们在这里不需要这样做。父项 RTE 的其他
	 * 基础设施必须转换为匹配子表的列顺序，我们将在下面进行，
	 * 因此“平坦”复制就足够了。
	 */
	fc_childrte = makeNode(RangeTblEntry);
	memcpy(fc_childrte, fc_parentrte, sizeof(RangeTblEntry));
	Assert(fc_parentrte->rtekind == RTE_RELATION); /* 否则这就不太可信 */
	fc_childrte->relid = fc_childOID;
	fc_childrte->relkind = fc_childrel->rd_rel->relkind;
	/* 一个分区子项将需要进一步扩展。 */
	if (fc_childrte->relkind == RELKIND_PARTITIONED_TABLE)
	{
		Assert(fc_childOID != fc_parentOID);
		fc_childrte->inh = true;
	}
	else
		fc_childrte->inh = false;
	fc_childrte->requiredPerms = 0;
	fc_childrte->securityQuals = NIL;

	/* 将尚未完全填充的子项 RTE 链接到数据结构中 */
	fc_parse->rtable = lappend(fc_parse->rtable, fc_childrte);
	fc_childRTindex = list_length(fc_parse->rtable);
	*fc_childrte_p = fc_childrte;
	*fc_childRTindex_p = fc_childRTindex;

	/*
	 * 为每个父/子对构建一个 AppendRelInfo 结构。
	 */
	fc_appinfo = make_append_rel_info(fc_parentrel, fc_childrel,
								   fc_parentRTindex, fc_childRTindex);
	fc_root->append_rel_list = lappend(fc_root->append_rel_list, fc_appinfo);

	/* tablesample 可能为 null，但仍要复制 */
	fc_childrte->tablesample = copyObject(fc_parentrte->tablesample);

	/*
	 * 为子项构建一个别名子句，我们也可以将其用作 eref。
	 * 这很重要，以便 EXPLAIN 将为子表列打印正确的列别名。
	 * （由于 ruleutils.c 没有任何简单的方法来重新关联
	 * 父列和子列，因此我们必须在一开始就正确获取子列
	 * 别名。请注意，设置 childrte->alias 强制
	 * ruleutils.c 使用这些列名，否则它不会使用。）
	 */
	fc_child_tupdesc = RelationGetDescr(fc_childrel);
	fc_parent_colnames = fc_parentrte->eref->colnames;
	fc_child_colnames = NIL;
	for (int fc_cattno = 0; fc_cattno < fc_child_tupdesc->natts; fc_cattno++)
	{
		Form_pg_attribute fc_att = TupleDescAttr(fc_child_tupdesc, fc_cattno);
		const char *fc_attname;

		if (fc_att->attisdropped)
		{
			/* 为已删除的列始终插入一个空字符串 */
			fc_attname = "";
		}
		else if (fc_appinfo->parent_colnos[fc_cattno] > 0 &&
				 fc_appinfo->parent_colnos[fc_cattno] <= list_length(fc_parent_colnames))
		{
			/* 复制父列的查询分配名称 */
			fc_attname = strVal(list_nth(fc_parent_colnames,
									  fc_appinfo->parent_colnos[fc_cattno] - 1));
		}
		else
		{
			/* 新列，直接使用其真实名称 */
			fc_attname = NameStr(fc_att->attname);
		}
		fc_child_colnames = lappend(fc_child_colnames, makeString(pstrdup(fc_attname)));
	}

	/*
	 * 我们只是为每个子项复制父项的表别名。如果计划
	 * 被打印，ruleutils.c 必须整理出唯一的表别名来
	 * 使用，这一点它可以处理。
	 */
	fc_childrte->alias = fc_childrte->eref = makeAlias(fc_parentrte->eref->aliasname,
												 fc_child_colnames);

	/*
	 * 将列权限位图转换为子项的 attnums（我们
	 * 必须在此之前构建 translated_vars 列表）。但如果
	 * 这是父表，我们可以直接复制父项的位图。
	 *
	 * 注意：即使执行器不会对子项 RTE 运行任何
	 * 权限检查，我们也需要这样做。插入的列/更新的列
	 * 位图可能会被检查以用于触发器触发目的。
	 */
	if (fc_childOID != fc_parentOID)
	{
		fc_childrte->selectedCols = fc_translate_col_privs(fc_parentrte->selectedCols,
													 fc_appinfo->translated_vars);
		fc_childrte->insertedCols = fc_translate_col_privs(fc_parentrte->insertedCols,
													 fc_appinfo->translated_vars);
		fc_childrte->updatedCols = fc_translate_col_privs(fc_parentrte->updatedCols,
													fc_appinfo->translated_vars);
	}
	else
	{
		fc_childrte->selectedCols = bms_copy(fc_parentrte->selectedCols);
		fc_childrte->insertedCols = bms_copy(fc_parentrte->insertedCols);
		fc_childrte->updatedCols = bms_copy(fc_parentrte->updatedCols);
	}

	/*
	 * 在各自的 PlannerInfo 数组中存储 RTE 和 appinfo，
	 * 调用者必须已经为此分配了空间。
	 */
	Assert(fc_childRTindex < fc_root->simple_rel_array_size);
	Assert(fc_root->simple_rte_array[fc_childRTindex] == NULL);
	fc_root->simple_rte_array[fc_childRTindex] = fc_childrte;
	Assert(fc_root->append_rel_array[fc_childRTindex] == NULL);
	fc_root->append_rel_array[fc_childRTindex] = fc_appinfo;

	/*
	 * 如果父项被标记为 FOR UPDATE/SHARE，则构建 PlanRowMark。
	 */
	if (fc_top_parentrc)
	{
		PlanRowMark *fc_childrc = makeNode(PlanRowMark);

		fc_childrc->rti = fc_childRTindex;
		fc_childrc->prti = fc_top_parentrc->rti;
		fc_childrc->rowmarkId = fc_top_parentrc->rowmarkId;
		/* 重新选择 rowmark 类型，因为 relkind 可能与父项不匹配 */
		fc_childrc->markType = select_rowmark_type(fc_childrte,
												fc_top_parentrc->strength);
		fc_childrc->allMarkTypes = (1 << fc_childrc->markType);
		fc_childrc->strength = fc_top_parentrc->strength;
		fc_childrc->waitPolicy = fc_top_parentrc->waitPolicy;

		/*
		 * 我们将分区子表的 RowMarks 标记为父 RowMarks，以便
		 * 执行器忽略它们（除非它们的存在意味着将
		 * 以适当的模式锁定子表）。
		 */
		fc_childrc->isParent = (fc_childrte->relkind == RELKIND_PARTITIONED_TABLE);

		/* 将子项的 rowmark 类型包含在顶层父项的 allMarkTypes 中 */
		fc_top_parentrc->allMarkTypes |= fc_childrc->allMarkTypes;

		fc_root->rowMarks = lappend(fc_root->rowMarks, fc_childrc);
	}

	/*
	 * 如果我们正在创建查询目标关系的子项（仅在
	 * UPDATE/DELETE/MERGE 中可能），将其添加到
	 * all_result_relids，以及适当的 leaf_result_relids，并确保我们生成
	 * 所需的行标识数据。
	 */
	if (bms_is_member(fc_parentRTindex, fc_root->all_result_relids))
	{
		/* 好的，将子项记录为结果关系。 */
		fc_root->all_result_relids = bms_add_member(fc_root->all_result_relids,
												 fc_childRTindex);

		/* 非叶分区不需要任何行标识信息。 */
		if (fc_childrte->relkind != RELKIND_PARTITIONED_TABLE)
		{
			Var		   *fc_rrvar;

			fc_root->leaf_result_relids = bms_add_member(fc_root->leaf_result_relids,
													  fc_childRTindex);

			/*
			 * 如果我们有任何子目标关系，假设它们都需要
			 * 生成一个垃圾“tableoid”列。（如果只有
			 * 一个子项在修剪后存活，我们实际上不需要这个，
			 * 但为了避免这个而费尽心机也不值得。）
			 */
			fc_rrvar = makeVar(fc_childRTindex,
							TableOidAttributeNumber,
							OIDOID,
							-1,
							InvalidOid,
							0);
			add_row_identity_var(fc_root, fc_rrvar, fc_childRTindex, "tableoid");

			/* 注册此子项所需的任何行标识列。 */
			add_row_identity_columns(fc_root, fc_childRTindex,
									 fc_childrte, fc_childrel);
		}
	}
}

/*
 * get_rel_all_updated_cols
 * 		返回给定“简单”关系中由此查询更新的列集。
 */
Bitmapset * get_rel_all_updated_cols(PlannerInfo *fc_root, RelOptInfo *fc_rel)
{
	Index		fc_relid;
	RangeTblEntry *fc_rte;
	Bitmapset  *fc_updatedCols,
			   *fc_extraUpdatedCols;

	Assert(fc_root->parse->commandType == CMD_UPDATE);
	Assert(IS_SIMPLE_REL(fc_rel));

	/*
	 * 我们获取查询结果关系的 updatedCols。
	 * 然后，如果有必要，我们将其映射到请求的关系的列号。
	 */
	fc_relid = fc_root->parse->resultRelation;
	fc_rte = planner_rt_fetch(fc_relid, fc_root);

	fc_updatedCols = fc_rte->updatedCols;

	if (fc_rel->relid != fc_relid)
	{
		RelOptInfo *fc_top_parent_rel = find_base_rel(fc_root, fc_relid);

		Assert(IS_OTHER_REL(fc_rel));

		fc_updatedCols = fc_translate_col_privs_multilevel(fc_root, fc_rel, fc_top_parent_rel,
													 fc_updatedCols);
	}

	/*
	 * 现在我们必须检查是否有任何依赖于 updatedCols 的
	 * 生成列，并将它们添加到结果中。
	 */
	fc_extraUpdatedCols = get_dependent_generated_columns(fc_root, fc_rel->relid,
													   fc_updatedCols);

	return bms_union(fc_updatedCols, fc_extraUpdatedCols);
}

/*
 * translate_col_privs
 *	  将表示每列权限的位图集从父关系的属性编号翻译为子项。
 *
 * 唯一的惊喜在于我们并不将父整个行
 * 引用翻译为子整个行引用。这将意味着要求
 * 所有子列的权限，这是过于严格的，因为
 * 查询实际上只会引用继承的列。因此
 * 我们为所有继承的列设置每列的位。
 */
static Bitmapset * fc_translate_col_privs(const Bitmapset *fc_parent_privs,
					List *fc_translated_vars)
{
	Bitmapset  *fc_child_privs = NULL;
	bool		fc_whole_row;
	int			fc_attno;
	ListCell   *fc_lc;

	/* 系统属性在所有表中具有相同的编号 */
	for (fc_attno = FirstLowInvalidHeapAttributeNumber + 1; fc_attno < 0; fc_attno++)
	{
		if (bms_is_member(fc_attno - FirstLowInvalidHeapAttributeNumber,
						  fc_parent_privs))
			fc_child_privs = bms_add_member(fc_child_privs,
										 fc_attno - FirstLowInvalidHeapAttributeNumber);
	}

	/* 检查父项是否具有整个行引用 */
	fc_whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,
							  fc_parent_privs);

	
/* 现在翻译常规用户属性，使用 vars 列表 */
	fc_attno = InvalidAttrNumber;
	foreach(fc_lc, fc_translated_vars)
	{
		Var		   *fc_var = lfirst_node(Var, fc_lc);

		fc_attno++;
		if (fc_var == NULL)		/* 忽略已删除的列 */
			continue;
		if (fc_whole_row ||
			bms_is_member(fc_attno - FirstLowInvalidHeapAttributeNumber,
						  fc_parent_privs))
			fc_child_privs = bms_add_member(fc_child_privs,
										 fc_var->varattno - FirstLowInvalidHeapAttributeNumber);
	}

	return fc_child_privs;
}

/*
 * translate_col_privs_multilevel
 * 递归地将 'parent_cols' 中包含的列编号转换为由 'rel' 给出的后代关系的列编号
 *
 * 请注意，由于这是基于 translate_col_privs，因此它会将整行引用扩展到所有继承的列。 这对当前的用法来说不是问题，但请注意。
 */
static Bitmapset * fc_translate_col_privs_multilevel(PlannerInfo *fc_root, RelOptInfo *fc_rel,
							   RelOptInfo *fc_parent_rel,
							   Bitmapset *fc_parent_cols)
{
	AppendRelInfo *fc_appinfo;

	/* 快速路径以处理简单情况。 */
	if (fc_parent_cols == NULL)
		return NULL;

	Assert(fc_root->append_rel_array != NULL);
	fc_appinfo = fc_root->append_rel_array[fc_rel->relid];
	Assert(fc_appinfo != NULL);

	/* 如果直接父级不是顶级父级，则递归。 */
	if (fc_appinfo->parent_relid != fc_parent_rel->relid)
	{
		RelOptInfo *fc_next_parent = find_base_rel(fc_root, fc_appinfo->parent_relid);

		fc_parent_cols = fc_translate_col_privs_multilevel(fc_root, fc_next_parent,
													 fc_parent_rel,
													 fc_parent_cols);
	}

	/* 现在为这个子节点进行翻译。 */
	return fc_translate_col_privs(fc_parent_cols, fc_appinfo->translated_vars);
}

/*
 * expand_appendrel_subquery
 *		为一个附加关系基表的子查询子节点添加“其他关系”RelOptInfos
 *
 * “rel” 是一个子查询关系，其rte->inh标志被设置，意味着它
 * 是一个已经被扁平化为一个附加关系的UNION ALL子查询，
 * 子查询列在root->append_rel_list中。我们需要为每个子关系构建
 * 一个RelOptInfo，以便我们能对它们进行扫描计划。
 */
static void fc_expand_appendrel_subquery(PlannerInfo *fc_root, RelOptInfo *fc_rel,
						  RangeTblEntry *fc_rte, Index fc_rti)
{
	ListCell   *fc_l;

	foreach(fc_l, fc_root->append_rel_list)
	{
		AppendRelInfo *fc_appinfo = (AppendRelInfo *) lfirst(fc_l);
		Index		fc_childRTindex = fc_appinfo->child_relid;
		RangeTblEntry *fc_childrte;
		RelOptInfo *fc_childrel;

		/* append_rel_list包含所有追加关系；忽略其他 */
		if (fc_appinfo->parent_relid != fc_rti)
			continue;

		/* 查找应该已经存在的子RTE */
		Assert(fc_childRTindex < fc_root->simple_rel_array_size);
		fc_childrte = fc_root->simple_rte_array[fc_childRTindex];
		Assert(fc_childrte != NULL);

		/* 构建子RelOptInfo。 */
		fc_childrel = build_simple_rel(fc_root, fc_childRTindex, fc_rel);

		/* 子可能本身就是一个继承关系，可以是表或子查询。 */
		if (fc_childrte->inh)
			expand_inherited_rtentry(fc_root, fc_childrel, fc_childrte, fc_childRTindex);
	}
}


/*
 * apply_child_basequals
 *		从父关系的资格中填充子关系的基本限制资格，
 *		使用appinfo进行翻译。
 *
 * 如果任何结果子句评估为常量false或NULL，我们
 * 返回false并且不应用任何资格。在这种情况下，调用者应该将关系标记为
 * 虚拟关系，因为不需要进行扫描。
 */
bool apply_child_basequals(PlannerInfo *fc_root, RelOptInfo *fc_parentrel,
					  RelOptInfo *fc_childrel, RangeTblEntry *fc_childRTE,
					  AppendRelInfo *fc_appinfo)
{
	List	   *fc_childquals;
	Index		fc_cq_min_security;
	ListCell   *fc_lc;

	/*
	 * 子关系的目标列表可能包含非Var表达式，
	 * 这意味着替换到资格中可能会产生常量简化的机会，
	 * 甚至可能产生伪常量资格。因此，
	 * 分别转换每个RestrictInfo以查看其是否简化为一个
	 * 常量或伪常量。（我们必须单独处理它们，以保持
	 * 每个资格的安全级别。）
	 */
	fc_childquals = NIL;
	fc_cq_min_security = UINT_MAX;
	foreach(fc_lc, fc_parentrel->baserestrictinfo)
	{
		RestrictInfo *fc_rinfo = (RestrictInfo *) lfirst(fc_lc);
		Node	   *fc_childqual;
		ListCell   *fc_lc2;

		Assert(IsA(fc_rinfo, RestrictInfo));
		fc_childqual = adjust_appendrel_attrs(fc_root,
										   (Node *) fc_rinfo->clause,
										   1, &fc_appinfo);
		fc_childqual = eval_const_expressions(fc_root, fc_childqual);
		/* 检查是否为纯常量 */
		if (fc_childqual && IsA(fc_childqual, Const))
		{
			if (((Const *) fc_childqual)->constisnull ||
				!DatumGetBool(((Const *) fc_childqual)->constvalue))
			{
				/* 限制减小为常量FALSE或NULL */
				return false;
			}
			/* 限制减小为常量TRUE，因此丢弃它 */
			continue;
		}
		/* 如果获得了AND子句，若是则扁平化它 */
		foreach(fc_lc2, make_ands_implicit((Expr *) fc_childqual))
		{
			Node	   *fc_onecq = (Node *) lfirst(fc_lc2);
			bool		fc_pseudoconstant;

			/* 检查是否为伪常量（没有Vars或易变函数） */
			fc_pseudoconstant =
				!contain_vars_of_level(fc_onecq, 0) &&
				!contain_volatile_functions(fc_onecq);
			if (fc_pseudoconstant)
			{
				/* 告诉 createplan.c 检查限制条件 */
				fc_root->hasPseudoConstantQuals = true;
			}
			/* 以适当的属性重构RestrictInfo */
			fc_childquals = lappend(fc_childquals,
								 make_restrictinfo(fc_root,
												   (Expr *) fc_onecq,
												   fc_rinfo->is_pushed_down,
												   fc_rinfo->outerjoin_delayed,
												   fc_pseudoconstant,
												   fc_rinfo->security_level,
												   NULL, NULL, NULL));
			/* 跟踪子资格中最低的安全级别 */
			fc_cq_min_security = Min(fc_cq_min_security, fc_rinfo->security_level);
		}
	}

	/*
	 * 除了从父级继承的资格之外，我们可能还有
	 * 与这个特定子节点关联的securityQuals。 （当前
	 * 这只能发生在源自UNION ALL的附加关系中；
	 * 继承子表没有自己的securityQuals，见
	 * expand_single_inheritance_child()。）将任何这样的securityQuals放入
	 * 子关系的baserestrictinfo中。这类似于
	 * process_security_barrier_quals()对父关系的处理，
	 * 只是我们不能从这种资格中进行一般推断，因为它们
	 * 并不适用于整个附加关系。
	 */
	if (fc_childRTE->securityQuals)
	{
		Index		fc_security_level = 0;

		foreach(fc_lc, fc_childRTE->securityQuals)
		{
			List	   *fc_qualset = (List *) lfirst(fc_lc);
			ListCell   *fc_lc2;

			foreach(fc_lc2, fc_qualset)
			{
				Expr	   *fc_qual = (Expr *) lfirst(fc_lc2);

				/* 在这里看到常量的可能性不高，因此不检查 */
				fc_childquals = lappend(fc_childquals,
									 make_restrictinfo(fc_root, fc_qual,
													   true, false, false,
													   fc_security_level,
													   NULL, NULL, NULL));
				fc_cq_min_security = Min(fc_cq_min_security, fc_security_level);
			}
			fc_security_level++;
		}
		Assert(fc_security_level <= fc_root->qual_security_level);
	}

	/*
	 * 好的，我们已获得此子关系的所有baserestrictinfo资格。
	 */
	fc_childrel->baserestrictinfo = fc_childquals;
	fc_childrel->baserestrict_min_security = fc_cq_min_security;

	return true;
}
